package com.lmabbe.generator;


import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.IDbQuery;
import com.baomidou.mybatisplus.generator.config.ITypeConvert;
import com.baomidou.mybatisplus.generator.config.converts.MySqlTypeConvert;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateExceptionHandler;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.io.FileWriter;
import java.io.Writer;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author lmabbe
 * @data 2021/3/28 16:12
 */
public class GeneratorApplication {

    private static String basePath = "F:\\MySpace\\lmabbe-admin\\lmabbe-generator\\src\\main\\resources";

    private static List<String> template = Arrays.asList(
            "add.vue.ftl",
            "edit.vue.ftl",
            "controller.java.ftl",
            "entity.java.ftl",
            "dto.java.ftl",
            "vo.java.ftl",
            "query.java.ftl",
            "converter.java.ftl",
            "serviceImpl.java.ftl",
            "index.vue.ftl",
            "mapper.java.ftl",
            "service.java.ftl",
            "index.ts.ftl");

    private static String outputDir = "F:\\MySpace\\lmabbe-admin\\lmabbe-generator\\src\\main";

    public static void main(String[] args) {

        try {
            Configuration cfg = new Configuration(Configuration.VERSION_2_3_31);
            cfg.setDirectoryForTemplateLoading(new File("F:\\MySpace\\lmabbe-admin\\lmabbe-generator\\src\\main\\resources\\template"));
            cfg.setDefaultEncoding("UTF-8");
            cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
            cfg.setLogTemplateExceptions(false);
            cfg.setWrapUncheckedExceptions(true);

            GeneratorApplication application = new GeneratorApplication();
            Map<String, Map> rootParam = application.getRootParam();
            Set<String> key = rootParam.keySet();
            for (String s : key) {
                for (String s1 : template) {
                    System.out.println(s1 + "----------" + s);
                    Template temp = cfg.getTemplate(s1);
                    String[] split = s1.split("\\.");
                    File file = null;
                    if (split[0].equals("add")) {
                        file = new File(outputDir + File.separator + s + File.separator + split[0] + s + "." + split[1]);
                    } else if (split[0].equals("edit")) {
                        file = new File(outputDir + File.separator + s + File.separator + split[0] + s + "." + split[1]);
                    } else if (split[0].equals("index") || split[0].equals("entity")) {
                        String index = split[0].equals("index") ? split[0] : s;
                        file = new File(outputDir + File.separator + s + File.separator + index + "." + split[1]);
                    } else if (split[0].equals("index") || split[0].equals("dto")) {
                        String index = split[0].equals("index") ? split[0] : s;
                        file = new File(outputDir + File.separator + s + File.separator + index + "DTO." + split[1]);
                    } else if (split[0].equals("index") || split[0].equals("vo")) {
                        String index = split[0].equals("index") ? split[0] : s;
                        file = new File(outputDir + File.separator + s + File.separator + index + "VO." + split[1]);
                    } else {
                        file = new File(outputDir + File.separator + s + File.separator + s + firstUpperCase(split[0]) + "." + split[1]);
                    }

                    file.getParentFile().mkdirs();
                    if (file.exists()) {
                        file.createNewFile();
                    }

                    Writer writer = new FileWriter(file);
                    temp.process(rootParam.get(s), writer);
                }
            }


        } catch (Exception e) {
            e.printStackTrace();
            System.out.println(e.getMessage());
        }
    }

    /**
     * 驼峰命名转换
     *
     * @param source
     * @return
     */
    public static String getHumpStr(String source) {
        if (StringUtils.isNotBlank(source)) {
            if (source.contains("_")) {
                Optional<String> reduced = Stream.of(source.split("_")).reduce((s1, s2) -> s1 + firstUpperCase(s2));
                if (reduced.isPresent()) {
                    return reduced.get();
                } else {
                    return "undefined";
                }
            } else {
                return firstUpperCase(source);
            }
        } else {
            return "undefined";
        }
    }

    /**
     * 首字母大写转换
     *
     * @param field
     * @return
     */
    public static String firstUpperCase(String field) {
        String first = field.substring(0, 1);
        return first.toUpperCase() + field.substring(1);
    }

    /**
     * 首字母小写转换
     *
     * @param field
     * @return
     */
    public static String firstLowerCase(String field) {
        String first = field.substring(0, 1);
        return first.toLowerCase() + field.substring(1);
    }

    /*private DataSourceConfig generatorDataSources() {
        DataSourceConfig dataSourceConfig = new DataSourceConfig();
        dataSourceConfig.setUrl("jdbc:mysql://lmabbe.com:3306/lmabbe-admin-test?useUnicode=true&useSSL=false&characterEncoding=utf8");
        dataSourceConfig.setDriverName("com.mysql.jdbc.Driver");
        dataSourceConfig.setUsername("root");
        dataSourceConfig.setPassword("Axvp3NsOc5Xcyqzl");
        return dataSourceConfig;
    }*/

    private DataSourceConfig generatorDataSources() {
        String url = "jdbc:mysql://lmabbe.com:3306/lmabbe-tenant-test?useUnicode=true&useSSL=false&characterEncoding=utf8";
        String username = "root";
        String password = "Axvp3NsOc5Xcyqzl";
        DataSourceConfig.Builder builder = new DataSourceConfig.Builder(url,username,password);
        builder.typeConvert(new MySqlTypeConvert());
        return builder.build();
    }


    public Map<String, Map> getRootParam() {
        Map res = new HashMap();
        try {
            DataSourceConfig dataSourceConfig = generatorDataSources();
            IDbQuery dbQuery = dataSourceConfig.getDbQuery();
            Connection conn = dataSourceConfig.getConn();
            PreparedStatement preparedStatement = conn.prepareStatement(dbQuery.tablesSql());
            ResultSet resultSet = preparedStatement.executeQuery();
            while (resultSet.next()) {
                String name = resultSet.getString("name");
                String comment = resultSet.getString("comment");
                System.out.println(name + "-------" + comment);

                String pkField = "";
                List<Map> list = new ArrayList<>();


                PreparedStatement fields = conn.prepareStatement(String.format(dbQuery.tableFieldsSql(), name));
                Set<String> imports = new HashSet<>();
                ResultSet resultSet1 = fields.executeQuery();
                while (resultSet1.next()) {
                    Map fieldMap = new HashMap();
                    fieldMap.put("fieldComment", resultSet1.getString("comment"));
                    boolean isKey = resultSet1.getString("key") != null && (!resultSet1.getString("key").equals(""));
                    if (isKey) {
                        pkField = changeToJavaFiled(resultSet1.getString("field"), false);
                    }
                    fieldMap.put("isPk", isKey);
                    fieldMap.put("fieldName", changeToJavaFiled(resultSet1.getString("field"), false));
                    System.out.println(resultSet1.getString("type"));
                    DateJava type = DateJava.getByDb(resultSet1.getString("type"));
                    fieldMap.put("dateType", type.getJava());
                    if (StringUtils.isNotBlank(type.getImports())) {
                        imports.add(type.getImports());
                    }
                    list.add(fieldMap);
                }

                List<String> dtoExcludeList = Arrays.asList(pkField, "valid", "createBy", "createTime", "modifyBy", "modifyTime");
                List<String> voExcludeList = Arrays.asList("valid", "createBy", "createTime", "modifyBy", "modifyTime");

                Map root = new HashMap();
                root.put("packageName", "com.lmabbe.tenant.objects.entity");
                root.put("tableComment", comment);
                root.put("tableName", changeToJavaFiled(name, true));
                root.put("tableName1", changeToJavaFiled(name, false));
                root.put("pkField", pkField);
                root.put("fields", list);
                root.put("dtofields", list.stream().filter(s -> !dtoExcludeList.contains(String.valueOf(s.get("fieldName")))).collect(Collectors.toList()));
                root.put("vofields", list.stream().filter(s -> !voExcludeList.contains(String.valueOf(s.get("fieldName")))).collect(Collectors.toList()));
                root.put("imports", imports);
                root.put("urlPre", "plat");
                res.put(changeToJavaFiled(name, true), root);
            }
            return res;
        } catch (Exception exception) {
            return null;
        }
    }

    private String changeToJavaFiled(String name, boolean isClass) {
        return isClass ? firstUpperCase(getHumpStr(name)) : firstLowerCase(getHumpStr(name));
    }

    public enum DateJava {
        VARCHAR("VARCHAR", "String", ""), CHAR("CHAR", "String", ""), INTEGER("INTEGER", "Integer", ""), INT("INT", "Integer", ""), TINYINT("TINYINT", "Boolean", ""), SMALLINT("SMALLINT", "Integer", ""), MEDIUMINT("MEDIUMINT", "Integer", ""), BIT("BIT", "Boolean", ""), BIGINT("BIGINT", "Long", ""), FLOAT("FLOAT", "Float", ""), DECIMAL("DECIMAL", "BigDecimal", "java.math.BigDecimal"), DATE("DATE", "Date", "java.util.Date"), DATETIME("DATETIME", "Date", "java.util.Date"), TIMESTAMP("TIMESTAMP", "Date", "java.util.Date"), JSON("JSON", "Map<String,Object>", "java.util.Map");

        private String db;
        private String java;
        private String imports;

        DateJava(String db, String java, String imports) {
            this.db = db;
            this.java = java;
            this.imports = imports;
        }

        public static DateJava getByDb(String from) {
            for (DateJava value : values()) {
                String s = from.trim().toLowerCase();
                if (s.contains("(")) {
                    s = s.substring(0, s.indexOf("("));
                }
                if (s.equals(value.getDb().trim().toLowerCase())) {
                    return value;
                }
            }
            return null;
        }

        public String getDb() {
            return db;
        }

        public String getJava() {
            return java;
        }

        public String getImports() {
            return imports;
        }
    }
}
